#define FLIP_TEXTURE_Y

float4x4 WorldITXf : WorldInverseTranspose;
float4x4 WvpXf : WorldViewProjection;
float4x4 WorldXf : World;
float4x4 ViewIXf : ViewInverse;

/// Point Lamp 0 ////////////
float3 Lamp0Pos : Position = {-0.5f,2.0f,1.25f};
float3 Lamp0Color : Specular = {1.0f,1.0f,1.0f};

// Ambient Light
float3 AmbiColor : Ambient = {0.07f,0.07f,0.07f};
float3 WarmColor  = {0.5f, 0.4f, 0.05f};
float3 CoolColor  = {0.05f, 0.05f, 0.6f};
float GlossTop  = 0.65;
float Specular  = 0.4;
float GlossDrop = 0.25;
float GlossEdge = 0.25;
float SpecExpon : SpecularPower = 5.0;
float Bump = 1.0; 
float ReflectionStrength = 0.5;

//////// COLOR & TEXTURE /////////////////////

float3 SurfaceColor : DIFFUSE = {1.0f, 1.0f, 1.0f}; 

texture NormalTexture;
texture EnvTexture : ENVIRONMENT;

sampler2D NormalSampler = sampler_state
{
    Texture = <NormalTexture>;
    FILTER = MIN_MAG_MIP_LINEAR;
    AddressU = Wrap;
    AddressV = Wrap;
};

samplerCUBE EnvSampler = sampler_state
{
    Texture = <EnvTexture>;
    FILTER = MIN_MAG_MIP_LINEAR;
    AddressU = Clamp;
    AddressV = Clamp;
    AddressW = Clamp;
};

/* data from application vertex buffer */
struct appdata
{
    float3 Position	: POSITION;
    float4 UV		: TEXCOORD0;
    float4 Normal	: NORMAL;
    float4 Tangent	: TANGENT0;
    float4 Binormal	: BINORMAL0;
};

/* data passed from vertex shader to pixel shader */
struct vertexOutput
{
    float4 HPosition	: POSITION;
    float2 UV		: TEXCOORD0;
	
    // The following values are passed in "World" coordinates since
    //   it tends to be the most flexible and easy for handling
    //   reflections, sky lighting, and other "global" effects.
    float3 LightVec	: TEXCOORD1;
    float3 WorldNormal	: TEXCOORD2;
    float3 WorldTangent	: TEXCOORD3;
    float3 WorldBinormal : TEXCOORD4;
    float3 WorldView	: TEXCOORD5;
};
 
///////// VERTEX SHADING /////////////////////

/*********** Generic Vertex Shader ******/

vertexOutput std_VS(appdata IN)
{
    vertexOutput OUT = (vertexOutput)0;
	
    OUT.WorldNormal = mul(IN.Normal,WorldITXf).xyz;
    OUT.WorldTangent = mul(IN.Tangent,WorldITXf).xyz;
    OUT.WorldBinormal = mul(IN.Binormal,WorldITXf).xyz;
	
    float4 Pos = float4(IN.Position.xyz,1);
    float3 worldPos = mul(Pos,WorldXf).xyz;
	
    OUT.LightVec = (Lamp0Pos - worldPos);
	
#ifdef FLIP_TEXTURE_Y

    OUT.UV = float2(IN.UV.x,(1.0-IN.UV.y));
	
#else /* !FLIP_TEXTURE_Y */

    OUT.UV = IN.UV.xy;
	
#endif /* !FLIP_TEXTURE_Y */

    OUT.WorldView = normalize(ViewIXf[3].xyz - worldPos);
    OUT.HPosition = mul(Pos,WvpXf);
	
    return OUT;
}

///////// PIXEL SHADING //////////////////////

// Utility function for gooch shading
float glossy_drop(	float v,
					uniform float top,
					uniform float bot,
					uniform float drop
				 )
{
    return (drop+smoothstep(bot,top,v)*(1.0-drop));
}

void gooch_shading(	vertexOutput IN,
					float3 LightColor,
					float3 Nn,
					float3 Ln,
					float3 Vn,
					out float3 DiffuseContrib,
					out float3 SpecularContrib
				  )
{
    float3 Hn = normalize(Vn + Ln);
    float ldn = dot(Ln,Nn);
    float hdn = dot(Hn,Nn);
    float4 litV = lit(ldn,hdn,SpecExpon);
    float goochFactor = (1.0 + ldn) / 2.0;
    float3 toneColor = lerp(CoolColor,WarmColor,goochFactor);
    DiffuseContrib = toneColor;
	
    // now to add glossiness...
    float spec = litV.y * litV.z;
    spec *= glossy_drop(spec,GlossTop,(GlossTop-GlossEdge),GlossDrop);
    SpecularContrib = spec * Specular * LightColor;
}

float4 std_PS(vertexOutput IN) : COLOR
{
    float3 diffContrib;
    float3 specContrib;
    float3 Ln = normalize(IN.LightVec);
    float3 Vn = normalize(IN.WorldView);
    float3 Nn = normalize(IN.WorldNormal);
    float3 Tn = normalize(IN.WorldTangent);
    float3 Bn = normalize(IN.WorldBinormal);
	
    float3 bump = Bump * (tex2D(NormalSampler,IN.UV).rgb - float3(0.5,0.5,0.5));
    Nn = Nn + bump.x*Tn + bump.y*Bn;
    Nn = normalize(Nn);
	
	gooch_shading(IN,Lamp0Color,Nn,Ln,Vn,diffContrib,specContrib);
	
    float3 diffuseColor = SurfaceColor;
    float3 result = specContrib+(diffuseColor*(diffContrib+AmbiColor));
    float3 R = -reflect(Vn,Nn);
    float3 reflColor = ReflectionStrength * texCUBE(EnvSampler,R.xyz).rgb;
    result += diffuseColor*reflColor;
	
    return float4(result,1);
}

///// TECHNIQUES /////////////////////////////
technique10 Main10
{
    pass p0
	{
        SetVertexShader( CompileShader( vs_4_0, std_VS() ) );
        SetGeometryShader( NULL );
        SetPixelShader( CompileShader( ps_4_0, std_PS() ) );
    }
}

technique Main
{
    pass p0
	{
        VertexShader = compile vs_2_0 std_VS();
		ZEnable = true;
		ZWriteEnable = true;
		ZFunc = LessEqual;
		AlphaBlendEnable = false;
		CullMode = None;
        PixelShader = compile ps_2_a std_PS();
    }
}